<!--
 * @Description: 文件库(只支持上传图片和zip压缩包)
 * @Autor: HuiSir<273250950@qq.com>
 * @Date: 2021-09-25 12:22:55
 * @LastEditTime: 2025-06-05 15:21:35
-->
<template>
    <div class="file-library-btn" @click="showFileLibraryBox = true">
        <slot />
    </div>
    <el-dialog v-model="showFileLibraryBox" @open="bindDialogOpen" @close="bindDialogClose" class="file-library-dialog"
        draggable width="80%" top="6vh" :append-to-body="true" :destroy-on-close="true" :title="`${typeLabel}库`">
        <div class="file-library-dialog-cont">
            <div class="left">
                <div class="btn-group">
                    <el-select v-model="fileQueryParams.group" placeholder="选择类目" filterable @change="fileGroupChange">
                        <el-option v-for="item in groups" :key="item.id" :label="item.name" :value="item.id">
                        </el-option>
                    </el-select>
                    <el-input v-model="fileQueryParams.name" placeholder="搜索..." clearable @input="bindSearchInput">
                        <template #prefix>
                            <fy-icon name="search" size="14" color="var(--el-text-color-placeholder)" />
                        </template>
                    </el-input>
                    <el-button :loading="leftLoading" @click="refresh">刷新</el-button>
                    <el-popconfirm title="你确定要删除当前文件吗?" @confirm="deleteFile">
                        <template #reference>
                            <el-button :disabled="selectedIndex < 0" type="danger" :loading="delBtnLoading">
                                <fy-icon name="delete" size="13" color="white" /> 删除
                            </el-button>
                        </template>
                    </el-popconfirm>
                </div>
                <!-- 图片列表 -->
                <!-- v-infinite-scroll 无限滚动 -->
                <div class="pic-list-box" ref="picList" v-if="type == 'pic'" v-infinite-scroll="nextPage"
                    :infinite-scroll-distance="30" :infinite-scroll-immediate="false" v-loading="leftLoading">
                    <div class="thumbnail upload" @click="popoverVisible = !popoverVisible">
                        <el-popover v-model:visible="popoverVisible" placement="bottom" :width="170" trigger="click">
                            <p style="text-align: center; margin-bottom: 10px">选择新增文件方式</p>
                            <div>
                                <el-button type="primary" link
                                    @click="popoverVisible = false; showFileUrlSetBox = true">
                                    URL插入
                                </el-button>
                                <el-upload style="display:inline-block;margin-left:10px;" action="/api/file/upload"
                                    accept="image/png, image/jpeg" :headers="uploadHeaders" :show-file-list="false"
                                    :on-success="handlePicSuccess" :before-upload="beforePicUpload">
                                    <el-button type="primary" size="small" @click="popoverVisible = false">直接上传
                                    </el-button>
                                </el-upload>
                            </div>
                            <template #reference>
                                <div class="pic-item">
                                    <fy-icon name="plus" size="30" color="var(--el-text-color-placeholder)" />
                                </div>
                            </template>
                        </el-popover>
                    </div>
                    <div v-for="(item, index) in fileList" :key="item.id" class="thumbnail"
                        :class="{ selected: selectedIndex == index }" @click="handleSelected(index)">
                        <el-image class="pic-item" fit="cover" :src="item.url" />
                    </div>
                </div>
                <!-- zip列表 -->
                <template v-else>
                    <el-table :data="fileList" highlight-current-row height="359" @current-change="handleFileListChange"
                        show-overflow-tooltip v-loading="leftLoading">
                        <el-table-column prop="name" label="文件名称" />
                        <el-table-column prop="size" label="文件大小" />
                        <el-table-column prop="createTime" label="创建时间" width="160" />
                    </el-table>
                    <el-pagination size="small" class="table-page-nav" :currentPage="fileQueryParams.page"
                        :page-size="pageLimit" :pager-count="5" layout="total, prev, pager, next, jumper"
                        :total="listTotal" hide-on-single-page @current-change="handleCurrChange">
                    </el-pagination>
                </template>
            </div>
            <div class="right">
                <template v-if="selectedIndex >= 0">
                    <h5>{{ typeLabel }}详情</h5>
                    <el-form label-width="75px" label-position="left">
                        <el-form-item v-if="type == 'pic'" label="图片预览">
                            <el-image class="preview-img" :src="currFile.url" :preview-src-list="[currFile.url]"
                                fit="contain" />
                            <fy-icon name="search" size="14" color="#fff" class="preview-icon" />
                        </el-form-item>
                        <el-form-item label="创建时间" class="text">
                            <span>{{ currFile.createTime }}</span>
                        </el-form-item>
                        <el-form-item label="文件大小" class="text">
                            <span>{{ currFile.size }}</span>
                        </el-form-item>
                        <el-form-item label="图片尺寸" class="text">
                            <span>{{ currFile.dimensions[0] }} × {{ currFile.dimensions[1] }}</span>
                        </el-form-item>
                        <el-form-item label="文件URL">
                            <el-input readonly v-model="currFile.url"></el-input>
                        </el-form-item>
                        <el-form-item label="文件名称">
                            <el-input v-model="currFile.name"></el-input>
                        </el-form-item>
                        <el-form-item label="选择类目">
                            <el-select v-model="currFile.group" filterable allow-create default-first-option
                                style="width:100%;">
                                <el-option v-for="item in groups" :key="item.id" :label="item.name" :value="item.id">
                                </el-option>
                            </el-select>
                            <div class="form-item-text">
                                Ps:可输入新类目</div>
                        </el-form-item>
                        <el-form-item label="文件描述">
                            <el-input v-model="currFile.desc" type="textarea"></el-input>
                        </el-form-item>
                    </el-form>
                </template>
                <template v-else>
                    <p>请从{{ typeLabel }}库中选择{{ typeLabel }}。</p>
                    <p>也可以
                        <el-upload class="upload-inline" action="/api/file/upload" accept="image/png, image/jpeg"
                            :headers="uploadHeaders" :show-file-list="false" :on-success="handlePicSuccess"
                            :before-upload="beforePicUpload">
                            <span>上传新{{ typeLabel }}</span>
                        </el-upload>
                        或
                        <span class="upload-inline" @click="showFileUrlSetBox = true">从URL插入</span>
                        。
                    </p>
                </template>
            </div>
        </div>
        <template v-if="selectedIndex >= 0" #footer>
            <span class="dialog-footer">
                <el-button @click="updateFileInfo" :loading="saveDetailLoading">保存详情</el-button>
                <el-button type="primary" @click="showFileLibraryBox = false; $emit('selected', currFile);">使用选中项
                </el-button>
            </span>
        </template>
    </el-dialog>
    <!-- 从URL插入 -->
    <el-dialog v-model="showFileUrlSetBox" title="从URL插入" :append-to-body="true" :destroy-on-close="true">
        <el-form ref="fileUrlSetFrom" label-width="80px" label-position="right" :model="fileUrlSetFromData"
            :rules="fileUrlSetFromRules">
            <el-form-item label="文件URL" prop="url">
                <el-input v-model="fileUrlSetFromData.url"></el-input>
            </el-form-item>
            <el-form-item label="文件名称" prop="name">
                <el-input v-model="fileUrlSetFromData.name"></el-input>
            </el-form-item>
            <el-form-item label="文件描述" prop="desc">
                <el-input v-model="fileUrlSetFromData.desc" type="textarea"></el-input>
            </el-form-item>
        </el-form>
        <template #footer>
            <span class="dialog-footer">
                <el-button @click="showFileUrlSetBox = false">取消</el-button>
                <el-button type="primary" @click="fileUrlSetSubmit">提交</el-button>
            </span>
        </template>
    </el-dialog>
</template>

<script lang="ts" setup>
import { computed, nextTick, reactive, ref, toRaw, watch, Ref, PropType } from 'vue'
import {
    getFileListByPage,
    removeFileById,
    getFileGroupList,
    addFileGroup,
    update as updateFile,
} from '@/api/file'
import { ElMessage } from 'element-plus'
import { debounce } from '@/ui/helpers'
import { TOKEN_OPTIONS } from 'settings'
import { useGetters } from '@/store'

const props = defineProps({
    /**
     * 文件类型，只支持上传图片和zip压缩包
     */
    type: {
        type: String as PropType<'pic' | 'zip'>,
        require: true,
        validator(value: string) {
            // 这个值必须匹配下列字符串中的一个
            const res = ['pic', 'zip'].includes(value)
            if (!res) {
                console.error('组件【fy-file-library】的参数【type】与预期值不符！')
            }
            return res
        },
    },
})

defineEmits(['selected'])

const picList = ref(null)
const leftLoading = ref(false)
const saveDetailLoading = ref(false)
const delBtnLoading = ref(false)
const fileList: Ref<any[]> = ref([])
const nomore = ref(false)
const listTotal = ref(0)
const pageLimit = props.type == 'pic' ? 24 : 8
// const pageLimit = 8
const typeLabel = props.type == 'pic' ? '图片' : '文件'
const fileUrlSetFrom = ref()
const uploadHeaders = { [TOKEN_OPTIONS.key]: useGetters().getToken }

// 分类列表
const groups = ref<{ id: string; name: string }[]>([])
const getGroup = async () => {
    const res = await getFileGroupList()
    groups.value = res.data.list
    groups.value.unshift({
        id: 'ALL',
        name: '全部类目',
    })
}

// 查詢参数
const fileQueryParams = reactive({
    group: 'ALL', //分组
    name: '', //搜索词（模糊搜索）
    page: 1, //当前页
})

// 类目切换
const fileGroupChange = () => {
    nomore.value = false
    fileQueryParams.name = ''
    fileQueryParams.page = 1
    getList()
}

const refresh = () => {
    nomore.value = false
    fileQueryParams.page = 1;
    ; (picList.value as any).scrollTop = 0
    getList()
}

// 搜索,防抖
const bindSearchInput = debounce(refresh, 300)

// 查询列表
async function getList() {
    if (nomore.value || leftLoading.value) return
    leftLoading.value = true
    // 清空当前选中项
    selectedIndex.value = -1
    const { page, name, group } = fileQueryParams

    const res = await getFileListByPage({
        page,
        name,
        group: group === 'ALL' ? '' : group,
        type: props.type,
        limit: pageLimit,
    })
    // 有数据
    const { list, total } = res.data

    listTotal.value = total
    fileList.value = props.type === 'zip' || page === 1 ? list : fileList.value.concat(list)

    // 请求结束
    if (!list?.length || list.length < pageLimit || fileList.value.length >= total) {
        nomore.value = true
    } else {
        nomore.value = false
    }
    await nextTick()
    leftLoading.value = false
}

// 页码切换
const nextPage = () => {
    if (leftLoading.value) return
    fileQueryParams.page++
    getList()
}

const handleCurrChange = (page: number) => {
    fileQueryParams.page = page
    getList()
}

// 当前所选
const selectedIndex = ref(-1)

// 选择
const handleSelected = async (index: number) => {
    if (selectedIndex.value === index) {
        selectedIndex.value = -1
        return
    }
    selectedIndex.value = index
}

// 当前文件
const currFile = computed(() =>
    selectedIndex.value >= 0 ? fileList.value[selectedIndex.value] : {}
)

// 弹窗
const showFileLibraryBox = ref(false)
const showFileUrlSetBox = ref(false)
const popoverVisible = ref(false)

const bindDialogOpen = () => {
    getGroup()
    getList()
}

// 关闭弹窗清除状态
const bindDialogClose = () => {
    selectedIndex.value = -1
    nomore.value = false
    listTotal.value = 0
    fileList.value = []
    Object.assign(fileQueryParams, {
        group: 'ALL',
        name: '',
        page: 1,
    })
}

// 文件url插入from
const fileUrlSetFromData = reactive({
    name: '',
    desc: '',
    url: '',
})

// 关闭弹窗清除状态
watch(showFileUrlSetBox, (val: boolean) => {
    if (!val) {
        Object.assign(fileUrlSetFromData, {
            name: '',
            desc: '',
            url: '',
        })
    }
})

// 文件URL from验证
const fileUrlSetFromRules = reactive({
    url: {
        validator: (rule: any, value: string, callback: (arg0?: Error | undefined) => void) => {
            if (!value || value === '') {
                callback(new Error('URL为必填项'))
            } else if (!/^https?:\/\/([\w-]+\.)+[\w-]+(:\d+)?(\/[\w-.\/?%&=]*)?$/.test(value)) {
                callback(new Error('URL格式有误'))
            } else {
                callback()
            }
        },
        required: true,
        trigger: 'blur',
    },
    name: {
        required: true,
        message: '文件名称为必填项',
        trigger: 'blur',
    },
})

// 文件URL from提交
const fileUrlSetSubmit = () => {
    // 验证
    fileUrlSetFrom.value.validate(async (valid: boolean) => {
        if (valid) {
            // 登录
            console.log(fileUrlSetFromData)
            return true
        }
        return false
    })
}

// 文件删除
const deleteFile = async () => {
    delBtnLoading.value = true
    const { msg } = await removeFileById((currFile.value as IFileParams).id)
    delBtnLoading.value = false
    // 刷新列表
    getList()
    ElMessage.success(msg)
}

// 文件详情-保存详情
const updateFileInfo = async () => {
    saveDetailLoading.value = true

    let { id, group, name, desc } = toRaw(currFile.value) as IFileParams

    // 选择全部类目
    if (group === 'ALL') {
        group = ''
    } else if (!groups.value.find((item) => item.id === group)) {
        // 新创建类目
        console.log('这是新创建的类目:', group)
        const { data } = await addFileGroup(group)
        group = data.id
    }

    // 更新
    await updateFile({ id, group, name, desc })

    saveDetailLoading.value = false
    ElMessage.success('保存成功!')
}

// 切换表格
const handleFileListChange = (val: IFileParams) => {
    if (val) {
        handleSelected(fileList.value.findIndex((item: IFileParams) => item.id == val.id))
    }
}

// 图片选择
const handlePicSuccess = (res: any, file: { raw: any }) => {
    console.log('11111111111111', res)
    //本地选择的缓存图片  URL.createObjectURL(file.raw)
    // userInfoFormData.value.avatar = URL.createObjectURL(file.raw)
}

// 图片上传前钩子
const beforePicUpload = (file: { type: string; size: number }) => {
    const allowType = file.type === 'image/jpeg' || file.type === 'image/png'
    const isLt2M = file.size / 1024 / 1024 < 2

    if (!allowType) {
        ElMessage.error('图片格式有误!')
        return false
    }
    if (!isLt2M) {
        ElMessage.error('上传图片不允许超过2MB!')
        return false
    }
    return true
}

// 暴露给ref
defineExpose({
    // 选择文件
    selectFile() {
        showFileLibraryBox.value = true
        getList()
    },
})
</script>

<style scoped lang="scss">
@use "./style.scss"
</style>
<style>
@media screen and (max-width:650px) {
    .file-library-dialog {
        width: 520px !important;
    }
}
</style>